//cineplex.cc

#include "cineplex.h"
#include <stdlib.h>

int NUM_CUSTOMERS;
int NUM_TIX_CLERKS;
int NUM_FOOD_CLERKS;
int NUM_TIX_TAKERS;
//int NUM_ROWS;
//int NUM_COLS;

//method to get the Cineplex singleton
Cineplex* getCineplex()
{
	if(!p_cineplex && singleExists == 0) //prevent segfaults DDD:
	{
		singleExists = 1;
		p_cineplex = new Cineplex();
	}
	return p_cineplex;
}

Cineplex* getCineplex(int run, int _c, int _tc, int _fc, int _tt, int pat)
{
	if(!p_cineplex && singleExists == 0) //prevent segfaults DDD:
	{
		singleExists = 1;
		p_cineplex = new Cineplex(run,_c,_tc,_fc,_tt, pat);
	}
	return p_cineplex;
}

void destroyCineplex()
{
	//only destroy 'fake' cineplexes for testing
	if(!p_cineplex->runSchedulers){
		p_cineplex = NULL;
		singleExists = 0;
	}
}

int getRandPercent()
{
	static int Init = 0;
	if(!Init)
	{
		srand ( time(NULL) );
		Init = 1;
	}
	return (int)( rand() % 100 );
}

Bathroom::Bathroom()
{
	brLock = new Lock("Bathroom Lock");
}

Cineplex::Cineplex()
{
	printf("USER INPUT:\nThis program will NOT try to fix invalid input!\n\n");
	printf("Enter number of customers (<= 40)");
	scanf("%d",&NUM_CUSTOMERS);getchar();
	printf("Enter number of TicketClerks (<= 5)");
	scanf("%d",&NUM_TIX_CLERKS);getchar();
	printf("Enter number of FoodClerks (<= 5)");
	scanf("%d",&NUM_FOOD_CLERKS);getchar();
	printf("Enter number of TicketTakers (<= 3)");
	scanf("%d",&NUM_TIX_TAKERS);getchar();
	//printf("Enter number of seating rows (<= 5)");
	//scanf("%d",&NUM_ROWS);getchar();
	//printf("Enter number of seating cols(<= 5)");
	//scanf("%d",&NUM_COLS);getchar();

	testPattern = 0;
	
	//create lists
	customers = new IterList();
	tixLines  = new IterList();
	tixClerks  = new IterList();
	foodLines  = new IterList();
	foodClerks  = new IterList();
	entryLines  = new IterList();
	ticketTakers  = new IterList();
	
	letPeopleIn = 0;
	runSchedulers = 1;
		
	//Locks
	tixLinesLock = new Lock("tixLinesLock");
	foodLinesLock  = new Lock("foodLinesLock");
	entryLinesLock  = new Lock("entryLinesLock");
	
	//Conditions
	waitNotEmpty = new Condition("waitNotEmpty");
	
	manager = new Manager("El Jefe");
	theater = new Theater();
	lobby = new Lobby("The Grand Foyer");
	tech  = new MovieTech("Technomagister");
	bathroom = new Bathroom();
	
	//GENERATE CUSTOMERS
	//(and the ticket pool while we're at it)
	int numGroups = 0;
	customersRunning = NUM_CUSTOMERS;
	ticketPool = (Ticket*) malloc(NUM_CUSTOMERS * sizeof(Ticket));
	tixPassedOut=0;
	popcornPool = (Popcorn*) malloc(NUM_CUSTOMERS * sizeof(Popcorn));
	popcornSold=0;
	sodaPool = (Soda*) malloc(NUM_CUSTOMERS * sizeof(Soda));
	sodaSold=0;
	IterList* subs = new IterList();
	for(int i = 0; i < NUM_CUSTOMERS; i++)
	{
		ticketPool[i].num = i;
		//char* cNum = std::itoa(i);
		char* name = new char;// = strcat("C #", cNum);
		sprintf(name, "C-%d", i);
		
		if(i == NUM_CUSTOMERS-1) //if last Customer
		{
			customers->Append(new Customer(name, numGroups+1, subs));
			if(!testPattern) printf("Customer %d is a leader with %d subs!\n", i, subs->getSize());
			//don't make new subs, no more customers
		}else if(subs->getSize() < 2) //Min # subs = 2
		{
			printf("Customer %s of group %d is waiting for the HeadCustomer.\n",name,numGroups+1);
			customers->Append(new Customer(name, numGroups+1, NULL));
			subs->Append(customers->Get(i));
		}else if(subs->getSize() < 7) //Max # subs = 7
		{
			if(getRandPercent() < 33) //Avg # subs = 2 + 3 = 5
			{
				printf("Customer %d is a leader with %d subs!\n", i, subs->getSize());
				customers->Append(new Customer(name, numGroups+1, subs));
				numGroups = numGroups + 1;
				subs = new IterList();
			}else{
				printf("Customer %s of group %d is waiting for the HeadCustomer.\n",name,numGroups+1);
				customers->Append(new Customer(name, numGroups+1, NULL));
				subs->Append(customers->Get(i));
			}
		}else{
			printf("Customer %d is a leader with %d subs!\n", i, subs->getSize());
			customers->Append(new Customer(name, numGroups+1, subs));
			numGroups = numGroups + 1;
			subs = new IterList();
		}
	}
	
	//GENERATE CLERKS
	for(int i = 0; i < NUM_TIX_CLERKS; i++)
	{
		char* name = new char;
		sprintf(name, "TC-%d", i);
		Line* currLine = new Line();
		TicketClerk* currClerk = new TicketClerk(name, currLine);
		tixLines->Append((void*) currLine);
		tixClerks->Append((void*) currClerk);
	}
	
	for(int i = 0; i < NUM_FOOD_CLERKS; i++)
	{
		char* name = new char;
		sprintf(name, "FC-%d", i);
		Line* currLine = new Line();
		FoodClerk* currClerk = new FoodClerk(name, currLine);
		foodLines->Append((void*) currLine);
		foodClerks->Append((void*) currClerk);
	}
	for(int i = 0; i < NUM_TIX_TAKERS; i++)
	{
		char* name = new char;
		sprintf(name, "TT-%d", i);
		Line* currLine = new Line();
		TicketTaker* currClerk = new TicketTaker(name, currLine);
		entryLines->Append((void*) currLine);
		ticketTakers->Append((void*) currClerk);
	}
	
	//INITIAL OUTPUT
	printf("NUMBER OF CUSTOMERS: %d\n", NUM_CUSTOMERS);
	printf("NUMBER OF GROUPS: %d\n", numGroups);
	printf("NUMBER OF TICKET CLERKS: %d\n", NUM_TIX_CLERKS);
	printf("NUMBER OF FOOD CLERKS: %d\n", NUM_FOOD_CLERKS);
	printf("NUMBER OF TICKET TAKERS: %d\n", NUM_TIX_TAKERS);
	printf("NUMBER OF SEATS: %d\n", NUM_ROWS*NUM_COLS);
	printf("NUMBER OF ROWS: %d\n", NUM_ROWS);
	printf("NUMBER OF COLS: %d\n", NUM_COLS);
}

Cineplex::Cineplex(int run, int _c, int _tc, int _fc, int _tt, int pat)
{
	NUM_CUSTOMERS = _c;
	NUM_TIX_CLERKS = _tc;
	NUM_FOOD_CLERKS = _fc;
	NUM_TIX_TAKERS = _tt;

	testPattern = pat;
	
	//create lists
	customers = new IterList();
	tixLines  = new IterList();
	tixClerks  = new IterList();
	foodLines  = new IterList();
	foodClerks  = new IterList();
	entryLines  = new IterList();
	ticketTakers  = new IterList();
	
	letPeopleIn = 0;
	runSchedulers = run;
		
	//Locks
	tixLinesLock = new Lock("tixLinesLock");
	foodLinesLock  = new Lock("foodLinesLock");
	entryLinesLock  = new Lock("entryLinesLock");
	
	//Conditions
	waitNotEmpty = new Condition("waitNotEmpty");
	
	manager = new Manager("El Jefe");
	theater = new Theater();
	lobby = new Lobby("The Grand Foyer");
	tech  = new MovieTech("Technomagister");
	bathroom = new Bathroom();
	
	//GENERATE CUSTOMERS
	//(and the ticket pool while we're at it)
	int numGroups = 0;
	customersRunning = NUM_CUSTOMERS;
	ticketPool = (Ticket*) malloc(NUM_CUSTOMERS * sizeof(Ticket));
	tixPassedOut=0;
	popcornPool = (Popcorn*) malloc(NUM_CUSTOMERS * sizeof(Popcorn));
	popcornSold=0;
	sodaPool = (Soda*) malloc(NUM_CUSTOMERS * sizeof(Soda));
	sodaSold=0;
	IterList* subs = new IterList();
	
	//size locked at threee for tests

	for(int i = 0; i < NUM_CUSTOMERS; i++)
	{
		ticketPool[i].num = i;
		//char* cNum = std::itoa(i);
		char* name = new char;// = strcat("C #", cNum);
		sprintf(name, "C-%d", i);
		
		if(i == NUM_CUSTOMERS-1) //if last Customer
		{
			customers->Append(new Customer(name, numGroups+1, subs));
			if(!testPattern) printf("Customer %d is a leader with %d subs!\n", i, subs->getSize());
			//don't make new subs, no more customers
		}else if(subs->getSize() < 2) //# subs = 2
		{
			customers->Append(new Customer(name, numGroups+1, NULL));
			subs->Append(customers->Get(i));
		}else{
			if(!testPattern) printf("Customer %d is a leader with %d subs!\n", i, subs->getSize());
			customers->Append(new Customer(name, numGroups+1, subs));
			numGroups = numGroups + 1;
			subs = new IterList();
		}
	}
	
	//GENERATE CLERKS
	for(int i = 0; i < NUM_TIX_CLERKS; i++)
	{
		char* name = new char;
		sprintf(name, "TC-%d", i);
		Line* currLine = new Line();
		TicketClerk* currClerk = new TicketClerk(name, currLine);
		tixLines->Append((void*) currLine);
		tixClerks->Append((void*) currClerk);
	}
	
	for(int i = 0; i < NUM_FOOD_CLERKS; i++)
	{
		char* name = new char;
		sprintf(name, "FC-%d", i);
		Line* currLine = new Line();
		FoodClerk* currClerk = new FoodClerk(name, currLine);
		foodLines->Append((void*) currLine);
		foodClerks->Append((void*) currClerk);
	}
	for(int i = 0; i < NUM_TIX_TAKERS; i++)
	{
		char* name = new char;
		sprintf(name, "TT-%d", i);
		Line* currLine = new Line();
		TicketTaker* currClerk = new TicketTaker(name, currLine);
		entryLines->Append((void*) currLine);
		ticketTakers->Append((void*) currClerk);
	}
	
	//INITIAL OUTPUT
	if(!testPattern) printf("NUMBER OF CUSTOMERS: %d\n", NUM_CUSTOMERS);
	if(!testPattern) printf("NUMBER OF GROUPS: %d\n", numGroups);
	if(!testPattern) printf("NUMBER OF TICKET CLERKS: %d\n", NUM_TIX_CLERKS);
	if(!testPattern) printf("NUMBER OF FOOD CLERKS: %d\n", NUM_FOOD_CLERKS);
	if(!testPattern) printf("NUMBER OF TICKET TAKERS: %d\n", NUM_TIX_TAKERS);
	if(!testPattern) printf("NUMBER OF SEATS: %d\n", NUM_ROWS*NUM_COLS);
	if(!testPattern) printf("NUMBER OF ROWS: %d\n", NUM_ROWS);
	if(!testPattern) printf("NUMBER OF COLS: %d\n", NUM_COLS);
}

Cineplex::~Cineplex()
{

}

void Cineplex::Start()
{
	for(int i = 0; i < NUM_TIX_CLERKS; i++)
	{
		//((TicketClerk*)tixClerks->Get(i))->Start();
		TicketClerk* agent = ((TicketClerk*)tixClerks->Get(i));
		Thread *t = new Thread(agent->myName);
		int ptr = (int) agent;
		t->Fork(startTC, ptr);
	}
	for(int i = 0; i < NUM_FOOD_CLERKS; i++)
	{
		//((FoodClerk*)foodClerks->Get(i))->Start();
		FoodClerk* agent = ((FoodClerk*)foodClerks->Get(i));
		Thread *t = new Thread(agent->myName);
		int ptr = (int) agent;
		t->Fork(startFC, ptr);
	}
	for(int i = 0; i < NUM_TIX_TAKERS; i++)
	{
		//((TicketClerk*)ticketTakers->Get(i))->Start();
		TicketTaker* agent = ((TicketTaker*)ticketTakers->Get(i));
		Thread *t = new Thread(agent->myName);
		int ptr = (int) agent;
		t->Fork(startTT, ptr);
	}
	//manager->Start();
	Thread *t = new Thread(manager->myName);
	int ptr = (int) manager;
	t->Fork(startManager, ptr);
	//lobby->Start();
	t = new Thread(lobby->myName);
	ptr = (int) lobby;
	t->Fork(startLobby, ptr);
	//tech->Start();
	t = new Thread(tech->myName);
	ptr = (int) tech;
	t->Fork(startTech, ptr);
	for(int i = 0; i < NUM_CUSTOMERS; i++)
	{
		//((Customer*)customers->Get(i))->Start();
		Customer* agent = ((Customer*)customers->Get(i));
		Thread *t = new Thread(agent->myName);
		int ptr = (int) agent;
		t->Fork(startCustomer, ptr);
	}
}

static void startCustomer(int ptr)
{
	Customer* agent = (Customer*) ptr;
	agent->Start();
}

static void startTC(int ptr)
{
	TicketClerk* agent = (TicketClerk*) ptr;
	agent->Start();
}

static void startFC(int ptr)
{
	FoodClerk* agent = (FoodClerk*) ptr;
	agent->Start();
}

static void startTT(int ptr)
{
	TicketTaker* agent = (TicketTaker*) ptr;
	agent->Start();
}

static void startManager(int ptr)
{
	Manager* agent = (Manager*) ptr;
	agent->Start();
}

static void startTech(int ptr)
{
	MovieTech* agent = (MovieTech*) ptr;
	agent->Start();
}

static void startLobby(int ptr)
{
	Lobby* agent = (Lobby*) ptr;
	agent->Start();
}


Ticket::Ticket()
{
	num = 0;
}

Theater::Theater()
{
	seatsFilled = 0;
	totalSeats = NUM_ROWS * NUM_COLS;
	seatingLock = new Lock("seatingLock");
	movieStarted=false;
	
	seating = (Customer**) malloc(NUM_ROWS * NUM_COLS * sizeof(Customer*));
}

Theater::~Theater()
{

}

Popcorn::Popcorn()
{

}

Soda::Soda()
{

}
